package com.zlc.workflow.core.process.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zlc.common.common.exception.BusinessException;
import com.zlc.common.core.process.mapper.StoneProcessClassMapper;
import com.zlc.common.core.process.model.entity.StoneProcess;
import com.zlc.common.core.process.model.entity.StoneProcessClass;
import com.zlc.common.core.process.model.vo.StoneProcessClassVo;
import com.zlc.common.core.process.model.vo.StoneProcessVo;
import com.zlc.common.core.process.service.StoneProcessClassService;
import com.zlc.common.core.process.service.StoneProcessService;
import com.zlc.common.model.process.process.ProcessRequestParam;
import com.zlc.common.util.AssertUtils;
import com.zlc.common.util.BeanCopyUtils;
import com.zlc.common.util.JsonHelper;
import com.zlc.common.util.PlatUtils;
import com.zlc.common.util.wrapper.CollectionHelper;
import com.zlc.workflow.constant.ProcessStatusEnum;
import com.zlc.workflow.core.process.cmd.DeployProcessCmd;
import com.zlc.workflow.core.process.cmd.ParseJsonToXmlCmd;
import com.zlc.workflow.core.process.cmd.ParseXmlToJsonCmd;
import com.zlc.workflow.core.process.cmd.QueryProcessCmd;
import com.zlc.workflow.core.process.model.FlowProcess;
import com.zlc.workflow.core.process.model.ProcessClassEntity;
import com.zlc.workflow.core.process.model.ProcessDeployEntity;
import com.zlc.workflow.core.process.model.WorkProcessEntity;
import com.zlc.workflow.core.process.service.ProcessService;
import com.zlc.workflow.flow.NodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class ProcessServiceImpl implements ProcessService {

    @Autowired
    private DeployProcessCmd deployProcessCmd;
    @Autowired
    private QueryProcessCmd queryProcessCmd;
    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneProcessClassService stoneProcessClassService;
    @Autowired
    private StoneProcessClassMapper stoneProcessClassMapper;

    @PostConstruct
    private void postStruct(){
        log.info("流程定义服务启动!");
    }

    @Override
    public List<StoneProcessVo> queryList(ProcessRequestParam requestParam) {
        LambdaQueryWrapper<StoneProcess> wrapper = stoneProcessService.wrapper()
                .eq(StoneProcess::getClassId, requestParam.getClassId());
        if (!ObjectUtils.isEmpty(requestParam.getKeyWord())) {
            wrapper.and(wrapper1 -> wrapper1.like(StoneProcess::getProcessCode, requestParam.getKeyWord())
                    .or()
                    .like(StoneProcess::getProcessName, requestParam.getKeyWord()));
        }
        if (Boolean.TRUE.equals(requestParam.getNewFlag())) {
            wrapper.eq(StoneProcess::getNewVersionFlag, Boolean.TRUE);
        }
        wrapper.orderByAsc(StoneProcess::getCreateTime);
        return stoneProcessService.queryVoList(wrapper);
    }

    @Override
    public ProcessDeployEntity saveProcess(ProcessDeployEntity entity) {
        return deployProcessCmd.deploy(entity);
    }

    @Override
    public List<WorkProcessEntity> queryWorkProcessList() {
        LambdaQueryWrapper<StoneProcess> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StoneProcess::getStatus, ProcessStatusEnum.PUBLISH.getType());
        List<StoneProcessVo> stoneProcessVos = stoneProcessService.queryVoList(wrapper);
        List<StoneProcessVo> newProcessVos = buildProcessName(stoneProcessVos);
        //查询分类
        List<String> classIds = stoneProcessVos.stream().map(StoneProcessVo::getClassId).distinct().collect(Collectors.toList());
        List<StoneProcessClassVo> classVos = stoneProcessClassService.getByClassIds(classIds);

        return classVos.stream().flatMap(classVo->{
            WorkProcessEntity entity = new WorkProcessEntity();
            entity.setClassName(classVo.getClassName());

            List<StoneProcessVo> childProcessList = newProcessVos.stream().filter(process->process.getClassId().equals(classVo.getClassId()))
                    .sorted(Comparator.comparing(StoneProcessVo::getCreateTime)).collect(Collectors.toList());
            entity.setProcessList(childProcessList);
            return Stream.of(entity);
        }).sorted(Comparator.comparing(WorkProcessEntity::getClassName)).collect(Collectors.toList());
    }

    private List<StoneProcessVo> buildProcessName(List<StoneProcessVo> stoneProcessVos){
        Map<String, List<StoneProcessVo>> mapProcess = stoneProcessVos.stream().collect(Collectors.groupingBy(StoneProcessVo::getProcessCode));
        return mapProcess.entrySet().stream().map(map->{
            List<StoneProcessVo> processVos = map.getValue();
            if(processVos.size() > 1){
                processVos.forEach(p->p.setProcessName(p.getProcessName()+"("+p.getProcessVersion()+")"));
            }
            return processVos;
        }).flatMap(Collection::stream).sorted(Comparator.comparing(StoneProcessVo::getCreateTime)).collect(Collectors.toList());
    }

    @Override
    public String buildXmlContent(FlowProcess process) {
        return new ParseJsonToXmlCmd(process).execute();
    }

    @Override
    public FlowProcess buildJsonContent(String xmlContent) {
        return new ParseXmlToJsonCmd(xmlContent).execute();
    }

    @Override
    public ProcessDeployEntity getByProcessId(String processId) {
        return queryProcessCmd.query(processId);
    }

    @Override
    public void changeStatus(String processId, String status) {
        AssertUtils.isTrue(!ObjectUtils.isEmpty(status) && !ObjectUtils.isEmpty(processId),"流程id或状态不可为空");
        StoneProcessVo processVo = stoneProcessService.getByProcessId(processId);
        if(ObjectUtils.isEmpty(processVo)){
            throw new BusinessException("流程不存在");
        }
        ProcessStatusEnum statusEnum = ProcessStatusEnum.getType(status);
        if(ObjectUtils.isEmpty(statusEnum)){
            throw new BusinessException("状态错误");
        }
        if(ProcessStatusEnum.PUBLISH.getType().equals(processVo.getStatus()) && ProcessStatusEnum.DRAFT.equals(statusEnum)){
            throw new BusinessException("发布的流程不可重置为草稿");
        }
        processVo.setStatus(status);
        stoneProcessService.saveVo(processVo);
    }

    @Override
    public Map<String, String> getProcessJsonByProcessId(String processId) {
        StoneProcessVo processVo = stoneProcessService.getByProcessId(processId);
        String processContent = NodeUtils.getProcessContent(processVo.getContent());
        String json = JsonHelper.toJson(buildJsonContent(processContent));
        Map<String, String> result = new HashMap<>();
        result.put("jsonContent", json);
        result.put("xmlContent", processContent);
        return result;
    }

    @Override
    public ProcessClassEntity classTree() {
        ProcessClassEntity entity = new ProcessClassEntity();
        entity.setClassId("root");
        entity.setClassName("根目录");
        entity.setOrder(0);
        List<StoneProcessClassVo> processClassVos = stoneProcessClassService.queryVoList(new LambdaQueryWrapper<>());
        List<StoneProcessClassVo> rootClassVos = processClassVos.stream()
                .filter(i -> ObjectUtils.isEmpty(i.getParentClassId())).collect(Collectors.toList());
        for (StoneProcessClassVo root : rootClassVos) {
            ProcessClassEntity rootE = BeanCopyUtils.copyObj(root, ProcessClassEntity.class);
            loadChildClass(rootE, processClassVos);
            entity.getChild().add(rootE);
        }
        CollectionHelper.sortList(entity.getChild(), ProcessClassEntity::getOrder, CollectionHelper.SortEnum.ASC);
        return entity;
    }

    private void loadChildClass(ProcessClassEntity classEntity, List<StoneProcessClassVo> classVos) {
        for (StoneProcessClassVo child : classVos) {
            if (classEntity.getClassId().equals(child.getParentClassId())) {
                ProcessClassEntity childE = BeanCopyUtils.copyObj(child, ProcessClassEntity.class);
                loadChildClass(childE, classVos);
                classEntity.getChild().add(childE);
            }
        }
        CollectionHelper.sortList(classEntity.getChild(), ProcessClassEntity::getOrder, CollectionHelper.SortEnum.ASC);
    }

    @Override
    public ProcessClassEntity addClass(StoneProcessClassVo classVo) {
        classVo.setClassId(PlatUtils.uuid());
        String parentClassId = classVo.getParentClassId();
        int order = 0;
        LambdaQueryWrapper<StoneProcessClass> wrapper = stoneProcessClassService.wrapper();
        if (!ObjectUtils.isEmpty(parentClassId)) {
            wrapper.eq(StoneProcessClass::getParentClassId, parentClassId);
            List<StoneProcessClass> list = stoneProcessClassService.list(wrapper);
            order = list.size() + 1;
        } else {
            wrapper.isNull(StoneProcessClass::getParentClassId);
            List<StoneProcessClass> list = stoneProcessClassService.list(wrapper);
            order = list.size() + 1;
        }
        classVo.setOrder(order);
        StoneProcessClassVo newClassVo = stoneProcessClassService.saveVo(classVo);
        ProcessClassEntity entity = new ProcessClassEntity();
        entity.setClassId(classVo.getClassId());
        entity.setClassName(classVo.getClassName());
        entity.setOrder(order);
        entity.setChild(new ArrayList<>());
        return entity;
    }

    @Override
    public void saveClass(StoneProcessClassVo classVo) {
        String classId = classVo.getClassId();
        if (ObjectUtils.isEmpty(classId)) {
            return;
        }
        StoneProcessClassVo oldClassVo = stoneProcessClassService.getByClassId(classId);
        if (!ObjectUtils.isEmpty(oldClassVo)) {
            oldClassVo.setClassName(classVo.getClassName());
            stoneProcessClassService.saveVo(oldClassVo);
        }
    }

    @Override
    public void removeClass(String classId) {
        StoneProcessClassVo classVo = stoneProcessClassService.getByClassId(classId);
        if (ObjectUtils.isEmpty(classVo)) {
            return;
        }
        List<StoneProcessClassVo> child = stoneProcessClassService.getChildByParentId(classId);
        if (!ObjectUtils.isEmpty(child)) {
            throw new BusinessException("类别下存在子类别，请先删除子类别");
        }
        List<StoneProcessVo> configVos = stoneProcessService.getByClassId(classVo.getClassId());
        AssertUtils.isTrue(ObjectUtils.isEmpty(configVos), "类别下存在流程定义，请删除后再操作！");
        stoneProcessClassService.deleteById(classVo.getId());
    }

    @Override
    public void addProcessTest(String processId, String content) {
        StoneProcessVo processVo = stoneProcessService.getByProcessId(processId);
        processVo.setContent(content.getBytes(StandardCharsets.UTF_8));
        StoneProcess copy = BeanCopyUtils.copyObj(processVo, StoneProcess.class);
        stoneProcessService.updateById(copy);
    }
}
